package xyz.xtt.asset.service.impl;

import static java.util.stream.Collectors.groupingBy;
import static xyz.xtt.common.asset.AssertUtils.isNull;
import static xyz.xtt.common.asset.AssertUtils.isTrue;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.primitives.Longs;
import com.tongtong.share.request.AssistantMessageRequest;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.client.request.ActivityTypeEnum;
import xyz.xtt.asset.client.request.DeliverRequest;
import xyz.xtt.asset.client.request.ExecDeliverResult;
import xyz.xtt.asset.client.request.SendHttpResponse;
import xyz.xtt.asset.constants.AssetConstants;
import xyz.xtt.asset.constants.ErrorMessage;
import xyz.xtt.asset.entity.TblAssetAccount;
import xyz.xtt.asset.entity.TblAssetTransaction;
import xyz.xtt.asset.request.EveryDayMessage;
import xyz.xtt.asset.service.ICmpService;
import xyz.xtt.asset.service.IInnerService;
import xyz.xtt.asset.service.INftService;
import xyz.xtt.asset.service.ITblAssetAccountService;
import xyz.xtt.asset.service.ITblAssetTransactionService;
import xyz.xtt.asset.service.IThirdService;
import xyz.xtt.asset.utils.BigDecimalUtils;
import xyz.xtt.asset.vo.CommonResponse;
import xyz.xtt.asset.vo.DeilverCommonResponse;
import xyz.xtt.asset.vo.GmtParmsRequestVo;
import xyz.xtt.asset.vo.GmtResponseVo;
import xyz.xtt.asset.vo.TransferFromResult;
import xyz.xtt.equity.client.request.SendDeliverResultRequest;
import xyz.xtt.user.api.enums.UserStatus;
import xyz.xtt.user.client.response.UserInfoResponse;

@RefreshScope
@Service
@Slf4j
public class CmpServiceImpl implements ICmpService {
	@Autowired
	private IInnerService iInnerService;
	private static final String TEXT_PLAIN = "text/plain";
	private static final String CONTENT_TYPE = "Content-Type";
	@Autowired
	private INftService nftService;

	/**
	 * 要发放的元素的nft
	 */
	@Value("${activity.element.accountNo}")
	private Long[] elementAccountNo;

	/**
	 * 要发放的目标元素
	 */
	@Value("${activity.element.options}")
	private Integer[] elements;
	/**
	 * 元素权益id
	 */
	@Value("${activity.element.entitlementId}")
	private Long elementEntitlementId;

	@Value("${activity.element.perNum}")
	private Long perNum;

	// 男装
	@Value("${activity.suit.male}")
	private Integer[] male;

	// 女装
	@Value("${activity.suit.famale}")
	private Integer[] female;

	@Value("${activity.suit.url}")
	private String gmtUrl;

	@Value("${activity.suit.secret}")
	private String gmtSecret;

	@Value("${activity.everyday.assetId}")
	private Long everydayAssetId;

	@Value("${activity.suit.entitlementId}")
	private Long suitEntitlementId;

	@Value("${activity.everyday.account}")
	private Long everyAccount;

	@Value("${activity.everyday.msgtitle:365大礼包}")
	private String everyMsgTitle;

	@Value("${activity.everyday.msgExpiredAtDay:7}")
	private Integer everyMsgExpiredAtDay;

	@Value("${activity.everyday.msgContent:【通通伙伴】您的创世礼包权益已到账，您可以登录通通app购买礼包的页面查看详情}")
	private String everyMsgContent;

	@Value("${activity.everyday.picUrl:''}")
	private String everyMsgpicUrl;

	@Value("${user.info.thirdAuthCode}")
	private String thirdAuthCode;

	@Value("${activity.supergroup.entitlementId}")
	private Long superGroupEntitlementId;

	@Autowired
	private ITblAssetTransactionService iTblAssetTransactionService;

	@Autowired
	private IThirdService thirdService;
	// 女勋章
	@Value("${activity.insigne.famale}")
	private Integer insigneFamale;
	// 男勋章
	@Value("${activity.insigne.male}")
	private Integer insigneMale;

	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	private ITblAssetAccountService iTblAssetAccountService;

	@Value("${activity.send.superGroupSwitch:true}")
	private Boolean sendSuperGroupSwitch;

	@Value("${activity.send.suitSwitch:true}")
	private Boolean sendSuitSwitch;

	@Value("${activity.send.ElementSwitch:true}")
	private Boolean sendElementSwitch;

	@Value("${activity.send.ssistantMsgSwitch:true}")
	private Boolean sendAssistantMsgSwitch;

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Boolean sendEveryDayGriftbox(EveryDayMessage message) {
		Long ttUserId = message.getTtUserId();
		String orderNo = message.getOrderNo();
		isNull(ttUserId, ErrorMessage.TARGET_USER_EXIST, () -> log.info("sendElement message uid is null msg:{}", JSONUtil.toJsonStr(message)));
		UserInfoResponse info = iInnerService.userInfo(ttUserId);
		isNull(info, ErrorMessage.TARGET_USER_EXIST, () -> log.info("target user: {} is not exist msg:{}", ttUserId, JSONUtil.toJsonStr(message)));
		if (!Objects.equals(UserStatus.NORMAL.getCode(), info.getStatus())) {
			log.info("sendEveryDayGriftbox user Logoff uid:{},everydayAssetId:{},msg:{}", ttUserId, everydayAssetId);
			return Boolean.FALSE;
		}
		isTrue(StringUtils.isBlank(orderNo), ErrorMessage.ARGUMENT_NOT_VALID, () -> log.info("orderNo is blank msg:{}", JSONUtil.toJsonStr(message)));
		// 发放365大礼包元素
		BigDecimal amount = iTblAssetAccountService.amount(ttUserId, everydayAssetId);
		if (BigDecimalUtils.isGreater(amount)) {
			log.info("sendEveryDayGriftbox fail amount>0 uid:{},everydayAssetId:{},msg:{}", ttUserId, everydayAssetId);
			return Boolean.FALSE;
		}
		TransferFromResult transferFrom = transferEveryDayGriftboxNft(message, AssetConstants.CMP);
		log.info("sendEveryDayGriftbox transferFrom uid: {},msg:{},transferFrom:{}", ttUserId, JSONUtil.toJsonStr(message), JSONUtil.toJsonStr(transferFrom));
		// 发元素
		if (Objects.equals(sendElementSwitch, Boolean.TRUE)) {
			Boolean sendElementWithMessage = sendElementWithMessage(message);
			log.info("sendElementWithMessage uid: {},msg:{},sendElementWithMessage:{}", ttUserId, JSONUtil.toJsonStr(message), sendElementWithMessage);
		}
		// 发套装
		if (Objects.equals(sendSuitSwitch, Boolean.TRUE)) {
			Boolean sendSuitWithMessage = sendSuitWithMessage(message, transferFrom, info);
			log.info("sendSuitWithMessage uid: {},msg:{},sendSuitWithMessage:{}", ttUserId, JSONUtil.toJsonStr(message), sendSuitWithMessage);
		}
		// 开通超级群
		if (Objects.equals(sendSuperGroupSwitch, Boolean.TRUE)) {
			Boolean sendSuperGrupWithMessage = sendSuperGrupWithMessage(message);
			log.info("sendSuperGrupWithMessage uid:{},msg:{},sendSuitWithMessage:{}", ttUserId, JSONUtil.toJsonStr(message), sendSuperGrupWithMessage);
		}
		// 发通通小助手
		if (Objects.equals(sendAssistantMsgSwitch, Boolean.TRUE)) {
			AssistantMessageRequest batchMsg = AssistantMessageRequest
					.builder()
					.title(everyMsgTitle)
					.expiredAtDay(7)
					.content(everyMsgContent)
					.picUrl(StringUtils.EMPTY)
					.uid(Lists.newArrayList(ttUserId))
					.build();
			Boolean sendAssistantMsg = iInnerService.sendAssistantMsg(batchMsg);
			log.info("sendAssistantMsg uid:{},result:{}", ttUserId, sendAssistantMsg);
		}
		return transferFrom.isOk();
	}

	private TransferFromResult transferEveryDayGriftboxNft(EveryDayMessage message, String bizType) {
		Long ttUserId = message.getTtUserId();
		String orderNo = message.getOrderNo();
		TblAssetTransaction transaction = this.iTblAssetTransactionService.infoWithOrderNo(orderNo);
		if (Objects.nonNull(transaction)) {
			log.info("sendEveryDayGriftbox transaction is not null fail uid:{} msg:{}", ttUserId, JSONUtil.toJsonStr(message));
			return TransferFromResult.builder().result(false).build();
		}
		TblAssetAccount ownerAsset = this.iTblAssetAccountService.account(everydayAssetId, ttUserId);
		if (Objects.nonNull(ownerAsset)) {
			log.info("user receiver uid:{} asset: {}, msg:{}", ttUserId, everydayAssetId, JSONUtil.toJsonStr(message));
			return TransferFromResult.builder().result(false).build();
		}
		TransferFromResult transferFrom = nftService.transferFromWithActivity(everyAccount, ttUserId, orderNo, 1, 0L, bizType);
		isNull(transferFrom, ErrorMessage.UNKNOWN_ERROR, () -> log.info("transferFrom is null fail: {}, msg:{}", everydayAssetId, JSONUtil.toJsonStr(message)));
		isTrue(!transferFrom.isOk(), ErrorMessage.UNKNOWN_ERROR, () -> log
				.info("transferFrom result is false fail transferFrom {}, msg:{}", transferFrom, JSONUtil.toJsonStr(message)));
		return transferFrom;
	}

	private Boolean sendElementWithMessage(EveryDayMessage message) {
		Long ttUserId = message.getTtUserId();
		String orderNo = message.getOrderNo();
		List<TblAssetAccount> accounts = iTblAssetAccountService.accounts(ttUserId, elementAccountNo);
		if (CollectionUtil.isNotEmpty(accounts)) {
			log.info("sendSuitWithMessage message uid: {} received, msg:{},accounts:{}", ttUserId, JSONUtil.toJsonStr(message), JSONUtil.toJsonStr(accounts));
			return Boolean.FALSE;
		}
		DeilverCommonResponse deliverElementNft = deliverElementNft(ttUserId, everydayAssetId, elementEntitlementId, orderNo);
		return saveDeliver(ttUserId, everydayAssetId, elementEntitlementId, deliverElementNft);
	}

	private DeilverCommonResponse deliverElementNft(Long ttUserId, Long assetId, Long entitlementId, String orderNo) {
		boolean sameLength = elementAccountNo.length == elements.length;
		Runnable logs = () -> log
				.info("please check elementAccountNo and elements entitlementId:{},config {}!={},orderNo:{}", entitlementId, elementAccountNo.length, elements.length, orderNo);
		isTrue(!sameLength, ErrorMessage.UNKNOWN_ERROR, logs);
		ElementHandler handler = new ElementHandler(stringRedisTemplate, nftService, perNum, elements.length);
		DeilverCommonResponse send = handler.send(entitlementId, orderNo, elementAccountNo, ttUserId, AssetConstants.CMP);
		isTrue(!send.isOk(), ErrorMessage.UNKNOWN_ERROR, () -> log
				.info("deliverElementNft fail entitlementId:{},assetId:{},ttUserId:{},orderNo:{}", entitlementId, assetId, ttUserId, orderNo));
		SendHttpResponse deliverElementCfg = deliverElementCfg(ttUserId, assetId, entitlementId, orderNo, send.getIndex(), true);
		send.setThirdResponse(Lists.newArrayList(deliverElementCfg));
		return send;
	}

	private SendHttpResponse deliverElementCfg(Long ttUserId, Long assetId, Long entitlementId, String orderNo, int index, boolean first) {
		isTrue(index >= elements.length, ErrorMessage.UNKNOWN_ERROR, () -> log
				.info("please element config index:{},elements.length:{}", index, elements.length));
		Integer elementCfgId = elements[index];
		SendHttpResponse doExec = doExec(ttUserId, elementCfgId, IdUtil.getSnowflakeNextId());
		return doExec;
	}

	public SendHttpResponse doExec(Long uid, Integer cfgId, long requestId) {
		boolean result = false;
		String body = StringUtils.EMPTY;
		String requestUrl = gmtUrl;
		String secret = gmtSecret;
		GmtParmsRequestVo addnft = GmtParmsRequestVo.buildAddnft(cfgId, uid);
		String json = JSONUtil.toJsonStr(addnft);
		String encodedString = Base64.getEncoder().encodeToString(json.getBytes());
		String signStr = SecureUtil.md5(encodedString + secret);
		String bodyStr = signStr + encodedString;
		HttpRequest request = HttpRequest
				.newBuilder()
				.uri(URI.create(requestUrl))
				.header(CONTENT_TYPE, TEXT_PLAIN)
				.timeout(Duration.ofSeconds(10))
				.POST(HttpRequest.BodyPublishers.ofString(bodyStr, StandardCharsets.UTF_8))
				.build();
		try {
			HttpClient client = HttpClient.newHttpClient();
			HttpResponse<String> send = client.send(request, HttpResponse.BodyHandlers.ofString());
			int statusCode = send.statusCode();
			body = send.body();
			log.info("doExec uid:{},cfgId:{},gmtUrl:{},secret:{},bodyStr{},statusCode:{},body:{}", uid, cfgId, requestUrl, secret, bodyStr, statusCode, body);
			if (Objects.equals(200, statusCode)) {
				GmtResponseVo response = JSONUtil.toBean(body, GmtResponseVo.class);
				result = Objects.nonNull(response) && response.isOk();
			}
		} catch (IOException | InterruptedException e) {
			log.info("doExec uid:{},cfgId:{} ex:{}", uid, cfgId, e.getMessage(), e);
		}
		SendHttpResponse build = SendHttpResponse.builder().cfgId(cfgId).result(result).thirdResponse(body).requestId(requestId).build();
		return build;
	}

	private Boolean sendSuitWithMessage(EveryDayMessage message, TransferFromResult transferFrom, UserInfoResponse info) {
		Long ttUserId = message.getTtUserId();
		String orderNo = message.getOrderNo();
		List<SendHttpResponse> suitResponseList = Lists.newArrayList();
		Integer[] targetSuit = targetSuit(info);
		// 没有发过
		if (targetSuit.length == 0) {
			log.info("targetSuit.length == 0 uid:{} message:{}", ttUserId, JSONUtil.toJsonStr(message));
			return Boolean.FALSE;
		}
		List<SendHttpResponse> deliverSuitCfg = deliverSuitCfg(ttUserId, targetSuit, everydayAssetId, suitEntitlementId, orderNo);
		if (CollectionUtil.isNotEmpty(deliverSuitCfg)) {
			suitResponseList.addAll(deliverSuitCfg);
		}
		DeilverCommonResponse buildResult = DeilverCommonResponse.buildResult(transferFrom, suitResponseList);
		return saveDeliver(ttUserId, everydayAssetId, suitEntitlementId, buildResult);
	}

	public List<SendHttpResponse> deliverSuitCfg(Long ttUserId, Integer[] targetSuit, Long assetId, Long entitlementId, String orderNo) {
		isNull(targetSuit, ErrorMessage.UNKNOWN_ERROR, () -> log.info("user:{} targetSuit is null", ttUserId));
		List<SendHttpResponse> results = Lists.newArrayList();
		long requestId = IdUtil.getSnowflakeNextId();
		for (Integer cfg : targetSuit) {
			SendHttpResponse response = doExec(ttUserId, cfg, requestId);
			results.add(response);
		}
		return results;
	}

	private Boolean saveDeliver(Long targetUid, Long assetId, Long entitlementId, DeilverCommonResponse buildResult) {
		SendDeliverResultRequest request = SendDeliverResultRequest
				.builder()
				.appUserId(targetUid)
				.assetId(assetId)
				.entitlementId(entitlementId)
				.responeText(JSONUtil.toJsonStr(buildResult))
				.result(buildResult.isOk() ? 1 : 0)
				.build();
		Boolean saveDeliverResult = iInnerService.saveDeliverResult(request);
		return saveDeliverResult;
	}

	private Integer[] targetSuit(UserInfoResponse info) {
		Integer gender = info.getGender();
		return targetSuit(gender);
	}

	private Integer[] targetSuit(Integer gender) {
		if (Objects.equals(AssetConstants.GENDER_MALE, gender)) {
			return male;
		} else if (Objects.equals(AssetConstants.GENDER_FEMALE, gender)) {
			return female;
		}
		return new Integer[] {};
	}

	private Boolean sendSuperGrupWithMessage(EveryDayMessage message) {
		Long ttUserId = message.getTtUserId();
		Boolean deliverSuperGroup = deliverSuperGroup(ttUserId, superGroupEntitlementId, everydayAssetId);
		log.info("deliverSuperGroup uid: {}, msg:{}, deliverSuperGroup:{}", ttUserId, JSONUtil.toJsonStr(message), deliverSuperGroup);
		return deliverSuperGroup;
	}

	private Boolean deliverSuperGroup(Long targetUid, Long entitlementId, Long assetId) {
		log.info("deliver SUPER_GROUP targetUid:{}, entitlementId:{}, assetId:{} 有资产没有兑换过超级群 ", targetUid, entitlementId, assetId);
		CommonResponse addGroupMaxMemberCount = thirdService.addGroupMaxMemberCount(targetUid);
		SendDeliverResultRequest request = SendDeliverResultRequest
				.builder()
				.appUserId(targetUid)
				.assetId(assetId)
				.entitlementId(entitlementId)
				.responeText(JSONUtil.toJsonStr(addGroupMaxMemberCount))
				.result(addGroupMaxMemberCount.isOk() ? 1 : 0)
				.build();
		Boolean saveDeliverResult = iInnerService.saveDeliverResult(request);
		return saveDeliverResult;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean deliver(DeliverRequest deliverRequest) {
		Long targetUid = deliverRequest.getTargetUid();
		Long entitlementId = deliverRequest.getEntitlementId();
		Long assetId = deliverRequest.getAssetId();
		String orderNo = deliverRequest.getOrderNo();
		String acType = deliverRequest.getActivityType();
		String externParm = deliverRequest.getExternParm();
		UserInfoResponse info = iInnerService.userInfo(targetUid);
		isNull(info, ErrorMessage.TARGET_USER_EXIST, () -> log.info("target user: {} is not exist", targetUid));
		if (!Objects.equals(UserStatus.NORMAL.getCode(), info.getStatus())) {
			log.info("deliver user Logoff uid:{},everydayAssetId:{},msg:{}", targetUid, everydayAssetId);
			return Boolean.FALSE;
		}
		TblAssetAccount targetAccount = this.iTblAssetAccountService.account(assetId, targetUid);
		isNull(targetAccount, ErrorMessage.ASSET_NOT_EXIST, () -> log.info("target targetAccount targetUid:{}, assetId:{} is not exist", targetUid, assetId));
//		TblAssetEntitlement entitlement = this.iTblAssetEntitlementService.info(entitlementId);
//		isNull(entitlement, ErrorMessage.ENTITLEMENT_NOT_EXIST, () -> log.info("target entitlementId: {} is not exist", entitlementId));
//		Runnable notEqualLog = () -> log.info("target entitlementId:{} assetId:{} not equal", entitlementId, assetId);
//		isTrue(!Objects.equals(assetId, entitlement.getAssetId()), ErrorMessage.ARGUMENT_NOT_VALID, notEqualLog);
		ActivityTypeEnum activityType = ActivityTypeEnum.convert(acType);
		isNull(activityType, ErrorMessage.ACTIVITIY_TYPE_NOT_EXIST, () -> log.info("deliver fail: {}", JSONUtil.toJsonStr(deliverRequest)));
//		TblAssetEntitlementDeliver deliver = iTblAssetEntitlementDeliverService.deliver(entitlementId, targetUid);
		ExecDeliverResult beforeResult = deliverRequest.getBeforeResult();
		// 兑换成功的，不允许在兑了
		isTrue((Objects.equals(beforeResult, ExecDeliverResult.YES)), ErrorMessage.ENTITLEMENT_DELIVER, () -> log
				.info("deliver SUPER_GROUP targetUid:{},entitlementId:{},assetId:{} 对应的权益已兑换", targetUid, entitlementId, assetId));
		String beforeReqponse = deliverRequest.getBeforeReqponse();
		log.info("targetUid:{}, entitlementId:{}, assetId:{}, activityType:{} noDeliver", targetUid, entitlementId, assetId, activityType);
		if (Objects.equals(activityType, ActivityTypeEnum.SUIT)) {
			log
					.info("deliver SUIT param: {} targetUid:{}, entitlementId:{}, assetId:{}", JSONUtil
							.toJsonStr(deliverRequest), targetUid, entitlementId, assetId);
			// 性别 0:男、1：女、2：未知
			// 计算本次应该发的数据
			// 兑换失败 分两种 余额和权益都要考虑
			// 1、有nft没兑过
			// 2、有nft兑失败了
			Integer gender = info.getGender();
			return deleverSuitExec(targetUid, entitlementId, assetId, orderNo, targetAccount, beforeReqponse, gender, beforeResult);
		} else if (Objects.equals(activityType, ActivityTypeEnum.SUIT_GENDER)) {
			log.info("deliver SUIT_GENDER param: {}", JSONUtil.toJsonStr(deliverRequest));
			Integer gender = suitGender(externParm);
			return deleverSuitExec(targetUid, entitlementId, assetId, orderNo, targetAccount, beforeReqponse, gender, beforeResult);
		} else if (Objects.equals(activityType, ActivityTypeEnum.ELEMENT)) {
			log
					.info("deliver ELEMENT param: {} targetUid:{}, entitlementId:{}, assetId:{}", JSONUtil
							.toJsonStr(deliverRequest), targetUid, entitlementId, assetId);
			// 有没有余额
			TblAssetAccount accountAtFirst = iTblAssetAccountService.accountsAtFirst(targetUid, elementAccountNo);
			// 没有余额
			if (Objects.isNull(accountAtFirst)) {
				// 1、有nft没兑过
				// 2、有nft兑失败了
				if (Objects.equals(ExecDeliverResult.NO, beforeResult)) {
					log.info("deliver ELEMENT targetUid:{}, entitlementId:{}, assetId:{} 没有资产 没兑换过权益", targetUid, entitlementId, assetId);
					// nft帐户有余额，但没有兑记录，只发元素就可以
					DeilverCommonResponse deliverElementNft = deliverElementNft(targetUid, assetId, entitlementId, orderNo);
					saveDeliver(targetUid, assetId, entitlementId, deliverElementNft);
					return deliverElementNft.isOk();
				} else if (Objects.equals(ExecDeliverResult.FAIL, beforeResult)) {
					// 兑换失败 分两种 余额和权益都要考虑
					// 1 没余额, 没权益
					// 2 有余额, 没权益
					// 3 没余额, 有权益 这种不会出现
					DeilverCommonResponse response = JSONUtil.toBean(beforeReqponse, DeilverCommonResponse.class);
					isNull(response, ErrorMessage.BEFORE_RESULT_ISNULL, () -> log
							.info("deliver ELEMENT targetUid:{}, entitlementId:{}, assetId:{} before is null", targetUid, entitlementId, assetId));
					List<SendHttpResponse> list = response.getThirdResponse();
					Map<Boolean, List<SendHttpResponse>> resultGroup = list.stream().collect(groupingBy(SendHttpResponse::getResult));
					Integer[] failArray = resultGroup
							.getOrDefault(Boolean.FALSE, Lists.newArrayList())
							.stream()
							.map(SendHttpResponse::getCfgId)
							.toArray(Integer[]::new);
					TransferFromResult transferFromResult = response.getTransferFromResult();

					log
							.info("targetUid:{},entitlementId:{},assetId:{},failArray len:{} transferFromResult:{}", targetUid, entitlementId, assetId, failArray.length, transferFromResult
									.getResult());
					// 之前的转帐也有可能是失败的，有失败记录，本地和远程全失败
					if (Objects.equals(Boolean.FALSE, transferFromResult.isOk())) {
						log.info("deliver ELEMENT targetUid:{}, entitlementId:{}, assetId:{} 有资产 兑换过权益　全部失败", targetUid, entitlementId, assetId);
						DeilverCommonResponse deliverElementNft = deliverElementNft(targetUid, assetId, entitlementId, orderNo);
						updateDeliver(targetUid, assetId, entitlementId, deliverElementNft);
						return deliverElementNft.isOk();
					} else {
						log.info("deliver ELEMENT targetUid:{}, entitlementId:{}, assetId:{} 有资产 兑换过权益　第三方失败", targetUid, entitlementId, assetId);
						if (failArray.length > 0) {
							log
									.info("deliver ELEMENT targetUid:{}, entitlementId:{}, assetId:{}, failArray len:{} 有资产 兑换过权益 失败", targetUid, entitlementId, assetId, failArray.length);
							int index = response.getIndex();
							SendHttpResponse deliverElementCfg = deliverElementCfg(targetUid, assetId, entitlementId, acType, index, true);
							DeilverCommonResponse buildResult = DeilverCommonResponse.buildResult(transferFromResult, deliverElementCfg);
							updateDeliver(targetUid, assetId, entitlementId, buildResult);
							return buildResult.isOk();
						}
					}
					// 元素只有一个,要么成功,要么失败,不会有多个其中部分失败

				} else {
					log.info("ELEMENT targetUid:{}, entitlementId:{}, assetId:{} 没有资产也没有失败的兑换记录", targetUid, entitlementId, assetId);
				}
			} else {
				// 有余额没有兑换记录 只发道具不转nft
				DeilverCommonResponse buildResult = calcBeforeResponse(accountAtFirst, targetUid);
				SendHttpResponse deliverElementCfg = deliverElementCfg(targetUid, assetId, entitlementId, acType, buildResult.getIndex(), true);
				buildResult.setThirdResponse(Lists.newArrayList(deliverElementCfg));
				if (Objects.equals(beforeResult, ExecDeliverResult.NO)) {
					log.info("deliver ELEMENT targetUid:{}, entitlementId:{}, assetId:{}, 有余额　没有兑换记录", targetUid, entitlementId, assetId);
					saveDeliver(targetUid, assetId, entitlementId, buildResult);
					return buildResult.isOk();
					// 有余额，有失败的记录
				} else if (Objects.equals(beforeResult, ExecDeliverResult.FAIL)) {
					log.info("deliver ELEMENT targetUid:{}, entitlementId:{}, assetId:{}, 有余额　有失败兑换记录", targetUid, entitlementId, assetId);
					updateDeliver(targetUid, assetId, entitlementId, buildResult);
					return buildResult.isOk();
				}
			}

		} else if (Objects.equals(activityType, ActivityTypeEnum.INSIGNE)) {
//			String requestParam = entitlement.getRequestParam();
//			isTrue(StringUtils.isBlank(requestParam), ErrorMessage.ARGUMENT_NOT_VALID, () -> log.info("requestParam is null"));
			Integer gender = suitGender(externParm);
			Integer targetType = 0;
			if (Objects.equals(AssetConstants.GENDER_MALE, gender)) {
				targetType = insigneMale;
			} else if (Objects.equals(AssetConstants.GENDER_FEMALE, gender)) {
				targetType = insigneFamale;
			}
			isTrue(Objects.equals(0, targetType), ErrorMessage.ARGUMENT_NOT_VALID);
			log.info("deliver INSIGNE targetUid:{}, entitlementId:{}, assetId:{}, targetType:{}", targetUid, entitlementId, assetId, targetType);
			if (Objects.equals(beforeResult, ExecDeliverResult.NO)) {
				log.info("deliver INSIGNE targetUid:{}, entitlementId:{}, assetId:{} 有资产 没兑换过权益", targetUid, entitlementId, assetId);
				// nft帐户有余额，但没有兑记录，只发元素就可以
				SendHttpResponse thirdResponse = sendCommonCfg(targetUid, targetType, IdUtil.getSnowflakeNextId());
				TransferFromResult transferFromResult = TransferFromResult
						.builder()
						.result(Boolean.TRUE)
						.assetId(assetId)
						.from(0L)
						.to(targetAccount.getAccountNo())
						.num(1)
						.build();
				DeilverCommonResponse build = DeilverCommonResponse
						.builder()
						.transferFromResult(transferFromResult)
						.thirdResponse(Lists.newArrayList(thirdResponse))
						.build();
				saveDeliver(targetUid, assetId, entitlementId, build);
				return build.isOk();
			} else if (Objects.equals(ExecDeliverResult.FAIL, beforeResult)) {
				DeilverCommonResponse commonResult = JSONUtil.toBean(beforeReqponse, DeilverCommonResponse.class);
				isNull(commonResult, ErrorMessage.BEFORE_RESULT_ISNULL, () -> log
						.info("before result is null entitlementId: {},uid:{} is not exist", entitlementId, targetUid));
				// 1 没余额, 没权益 全部失败
				// 2 有余额, 没权益
				// 3 没余额, 有权益 这种不会出现
				if (Objects.equals(Boolean.FALSE, commonResult.isThirdResponse())) {
					log.info("before commonResult.isThirdResponse() {}", commonResult.isThirdResponse());
					SendHttpResponse thirdResponse = sendCommonCfg(targetUid, targetType, IdUtil.getSnowflakeNextId());
					DeilverCommonResponse build = DeilverCommonResponse
							.builder()
							.transferFromResult(commonResult.getTransferFromResult())
							.thirdResponse(Lists.newArrayList(thirdResponse))
							.build();
					updateDeliver(targetUid, assetId, entitlementId, build);
					return build.isOk();
				}
			} else {
				log.info("deliver INSIGNE targetUid:{}, entitlementId:{}, assetId:{} 没有资产也没有失败的兑换记录", targetUid, entitlementId, assetId);
			}
		} else if (Objects.equals(activityType, ActivityTypeEnum.SUPER_GROUP)) {
			if (Objects.equals(beforeResult, ExecDeliverResult.NO)) {
				return deliverSuperGroup(targetUid, entitlementId, assetId);
			} else {
				log.info("deliver SUPER_GROUP targetUid:{},entitlementId:{},assetId:{} 有资产没有兑换过超级群 操作失败 ", targetUid, entitlementId, assetId);
				CommonResponse before = JSONUtil.toBean(beforeReqponse, CommonResponse.class);
				isNull(before, ErrorMessage.ARGUMENT_NOT_VALID, () -> log
						.info("before result is fail entitlementId: {},uid:{},before:{}", entitlementId, targetUid, before));
				if (Objects.equals(ExecDeliverResult.FAIL, beforeResult)) {
					CommonResponse addGroupMaxMemberCount = thirdService.addGroupMaxMemberCount(targetUid);
					SendDeliverResultRequest request = SendDeliverResultRequest
							.builder()
							.appUserId(targetUid)
							.assetId(assetId)
							.entitlementId(entitlementId)
							.responeText(JSONUtil.toJsonStr(addGroupMaxMemberCount))
							.result(addGroupMaxMemberCount.isOk() ? 1 : 0)
							.build();
					Boolean saveDeliverResult = iInnerService.saveDeliverResult(request);
					return saveDeliverResult;
				}
			}
		}
		return Boolean.FALSE;
	}

	private DeilverCommonResponse calcBeforeResponse(TblAssetAccount toAccount, Long targetUid) {
		Long assetId = toAccount.getAssetId();
		TblAssetTransaction beforeTransaction = this.iTblAssetTransactionService.beforeTransactionWithBuyUidAndAssetId(targetUid, assetId);
		isNull(beforeTransaction, ErrorMessage.BEFORE_TRANSACTION_ISNULL, () -> log.info("uid:{}, assetId:{} before transaction is null", targetUid, assetId));
		String sellerUserId = beforeTransaction.getSellerUserId();
		// 计算之前交易的帐户，算出索引
		TblAssetAccount ownerAsset = this.iTblAssetAccountService.accountNoCheckAmount(assetId, Longs.tryParse(sellerUserId));
		isNull(beforeTransaction, ErrorMessage.ACCOUNT_NOT_EXIST, () -> log.info("uid:{}, assetId:{} before transaction is null", targetUid, assetId));
		int calcIndex = calcIndex(ownerAsset.getAccountNo());
		Long form = elementAccountNo[calcIndex];
		TransferFromResult transferFromResult = TransferFromResult
				.builder()
				.from(form)
				.to(toAccount.getAccountNo())
				.result(Boolean.TRUE)
				.assetId(assetId)
				.build();
		return DeilverCommonResponse.builder().index(calcIndex).transferFromResult(transferFromResult).thirdResponse(Lists.newArrayList()).build();
	}

	private int calcIndex(Long accountNo) {
		int index = 0;
		for (Long no : elementAccountNo) {
			if (Objects.equals(accountNo, no)) {
				break;
			}
			index++;
		}
		log.info("calcIndex ex len:{},index:{}", elementAccountNo.length, index);
		isTrue(index >= elementAccountNo.length, ErrorMessage.ACCOUNT_NOT_EXIST);
		return index;
	}

	private SendHttpResponse sendCommonCfg(Long uid, Integer cfgId, long requestId) {
		return doExec(uid, cfgId, requestId);
	}

	private Integer suitGender(String externParm) {
		isTrue(StringUtils.isBlank(externParm), ErrorMessage.ARGUMENT_NOT_VALID, () -> log.info("externParm is blank"));
		JSONObject bean = JSONUtil.toBean(externParm, JSONObject.class);
		isNull(bean, ErrorMessage.ARGUMENT_NOT_VALID, () -> log.info("externParm convert object is null"));
		Integer gender = bean.getInt("gender");
		boolean checkGender = Objects.equals(AssetConstants.GENDER_FEMALE, gender) || Objects.equals(AssetConstants.GENDER_MALE, gender);
		isTrue(!checkGender, ErrorMessage.ARGUMENT_NOT_VALID, () -> log.info("gender: {} invalid", gender));
		return gender;
	}

	private Boolean deleverSuitExec(Long targetUid, Long entitlementId, Long assetId, String orderNo, TblAssetAccount ownerAsset, String beforeReqponse,
			Integer gender, ExecDeliverResult beforeResult) {
		if (Objects.equals(beforeResult, ExecDeliverResult.NO)) {
			log.info("deleverSuitExec targetUid:{}, entitlementId:{}, assetId:{} 有资产 没兑换过权益", targetUid, entitlementId, assetId);
			Integer[] targetSuit = targetSuit(gender);
			if (targetSuit.length == 0) {
				log.info("deleverSuitExec targetUid:{}, entitlementId:{}, assetId:{}, targetSuit len==0 gender:{}", targetUid, entitlementId, assetId, gender);
				return Boolean.FALSE;
			}
			List<SendHttpResponse> deliverSuitCfg = deliverSuitCfg(targetUid, targetSuit, assetId, entitlementId, orderNo);
			TransferFromResult transferFromResult = TransferFromResult
					.builder()
					.result(Boolean.TRUE)
					.assetId(assetId)
					.from(0L)
					.to(ownerAsset.getAccountNo())
					.num(1)
					.build();
			DeilverCommonResponse build = DeilverCommonResponse.builder().transferFromResult(transferFromResult).thirdResponse(deliverSuitCfg).build();
			saveDeliver(targetUid, assetId, entitlementId, build);
			log.info("deliverSuit result: {}", JSONUtil.toJsonStr(build));
			return build.isOk();
		} else if (Objects.equals(beforeResult, ExecDeliverResult.FAIL)) {
			// 兑换失败 分两种 余额和权益都要考虑
			// 1 没余额, 权益失败
			// 2 有余额, 权益失败
			// 3 没余额, 有权益 这种不会出现
			DeilverCommonResponse response = JSONUtil.toBean(beforeReqponse, DeilverCommonResponse.class);
			List<SendHttpResponse> list = response.getThirdResponse();
			Map<Boolean, List<SendHttpResponse>> resultGroup = list.stream().collect(groupingBy(SendHttpResponse::getResult));
			List<SendHttpResponse> succ = resultGroup.getOrDefault(Boolean.TRUE, Lists.newArrayList());
			Integer[] failArray = resultGroup
					.getOrDefault(Boolean.FALSE, Lists.newArrayList())
					.stream()
					.map(SendHttpResponse::getCfgId)
					.toArray(Integer[]::new);
			log.info("targetUid:{}, entitlementId:{}, assetId:{}, failArray len:{}", targetUid, entitlementId, assetId, failArray.length);
			if (failArray.length > 0) {
				log.info("deleverSuitExec SUIT targetUid:{}, entitlementId:{}, assetId:{} 有资产 兑换过权益 失败", targetUid, entitlementId, assetId);
				// 内部会出处理套装失败的情况
				List<SendHttpResponse> deliverSuit = deliverSuitCfg(targetUid, failArray, assetId, entitlementId, orderNo);
				deliverSuit.addAll(succ);
				DeilverCommonResponse buildResult = DeilverCommonResponse.buildResult(response.getTransferFromResult(), deliverSuit);
				updateDeliver(targetUid, assetId, entitlementId, buildResult);
				log.info("deliverSuit result: {}", JSONUtil.toJsonStr(buildResult));
				return buildResult.isOk();
			}
		} else {
			log.info("SUIT targetUid:{}, entitlementId:{}, assetId:{} 没有资产也没有失败的兑换记录", targetUid, entitlementId, assetId);
		}
		return Boolean.FALSE;
	}

	private Boolean updateDeliver(Long targetUid, Long assetId, Long entitlementId, DeilverCommonResponse buildResult) {
		Boolean saveDeliver = saveDeliver(targetUid, assetId, entitlementId, buildResult);
		return saveDeliver;
//		TblAssetEntitlementDeliver buildDeliver = buildDeliver(targetUid, assetId, entitlementId, JSONUtil.toJsonStr(buildResult), buildResult.isOk() ? 1 : 0);
//		boolean saveDeliver = iTblAssetEntitlementDeliverService.saveOrUpdateByDeliver(buildDeliver, true);
//		return saveDeliver;
	}
}
